# Load required libraries
library(shiny)
library(networkD3)
library(readxl)
library(dplyr)
library(janitor)
library(ggplot2)
library(plotly)

# Load the datasets
airport_weather_file <- "airport_weather.xlsx"
bitre_otp_file <- "BITRE_OTP_2023_Master.xlsx"
airports_file <- "au-airports.csv"

# Read the data
airport_weather_data <- read_excel(airport_weather_file, sheet = NULL)
bitre_otp_data <- read_excel(bitre_otp_file, sheet = "2023")
airports_data <- read.csv(airports_file)

# Combine all weather sheets into one dataframe
weather_combined <- bind_rows(airport_weather_data)

# Clean up column names for airports data and rename for easier merging
airports_data_clean <- airports_data %>%
  filter(type %in% c("large_airport", "medium_airport")) %>%
  clean_names() %>%
  rename(
    airport_name = name,
    municipality = municipality,
    country = country_name,
    iso_code = iso_country,
    latitude = latitude_deg,
    longitude = longitude_deg
  )

# Clean up column names for weather data
weather_combined <- weather_combined %>% clean_names()

# Merge airport weather data with airport information on city/municipality
merged_data <- airports_data_clean %>%
  inner_join(weather_combined, by = c("municipality" = "name")) %>%
  mutate(airport_name_simplified = gsub("^([A-Za-z]+).*", "\\1", airport_name) %>% trimws())

# Clean up BITRE data and rename columns for easier merging
bitre_otp_clean <- bitre_otp_data %>%
  clean_names() %>%
  rename(
    arriving_port = arriving_port,
    departing_port = departing_port
  )

# Filter down BITRE data to match with merged airport data (only the relevant arriving and departing ports)
bitre_filtered <- bitre_otp_clean %>%
  mutate(arriving_port_simplified = gsub("^([A-Za-z]+).*", "\\1", arriving_port) %>% trimws()) %>%
  filter(arriving_port_simplified %in% merged_data$airport_name_simplified | departing_port %in% merged_data$airport_name_simplified)

# Merge BITRE data with merged_data on airport_name
final_merged <- merged_data %>%
  left_join(bitre_filtered, by = c("airport_name_simplified" = "arriving_port_simplified"), relationship = "many-to-many")

# Create Shiny app
ui <- fluidPage(
  titlePanel("2023 Australia Airline Performance Dashboard"),
  
  sidebarLayout(
    sidebarPanel(
      helpText("This dashboard shows the performance of airlines at different ports.(overall performance and bubble chart)"),
      selectInput(
        "airline", 
        "Select Airline:", 
        choices = c("All Airlines", setdiff(unique(final_merged$airline[!is.na(final_merged$airline)]), "All Airlines")), 
        selected = "All Airlines",
        multiple = FALSE,
        selectize = FALSE
      ),
      selectInput("port", "Select Port:", choices = unique(final_merged$airport_name[!is.na(final_merged$airport_name)])),
      selectInput("weather_factor", "Select Weather Factor for Bubble Size:",
                  choices = c("Average Annual Temperature (°C)" = "average_annual_temperature_c",
                              "Annual Average Humidity (%)" = "annual_average_humidity_percent",
                              "Annual Average Wind Speed (km/h)" = "annual_average_wind_speed_km_h",
                              "Annual Average Visibility (km)" = "annual_average_visibility_km"),
                  selected = "average_annual_temperature_c")
    ),
    
    mainPanel(
      tabsetPanel(
        tabPanel("Overall Performance", plotlyOutput("performancePlot")),
        tabPanel("Sankey Diagram", 
                 h3("Flow of Arrivals and Departures Between Australian Airports"),
                 p("This Sankey diagram shows the flow of flights between major Australian airports, including both arrivals and departures. The thickness of each line represents the volume of flights, helping you visualize the connections between different airports."),
                 sankeyNetworkOutput("sankeyPlot")),
        tabPanel("Bubble Chart", plotlyOutput("bubblePlot")),
        tabPanel("Interactive Map", plotlyOutput("mapPlot"))
      )
    )
  )
)

server <- function(input, output) {
  filtered_data <- reactive({
    filtered <- final_merged %>%
      filter((airline == input$airline | input$airline == "All Airlines") & airport_name == input$port)
    
    if (nrow(filtered) == 0) {
      showNotification("No data available for the selected combination of airline and port. Please select another combination.", type = "error")
    }
    
    return(filtered)
  })
  
  output$bubblePlot <- renderPlotly({
    filtered <- filtered_data()
    if (nrow(filtered) == 0) {
      return(NULL)
    }
    
    # Dynamically set the bubble size based on user-selected weather factors
    size_column <- filtered[[input$weather_factor]]
    
    bubble_plot <- ggplot(filtered, aes(
      x = on_time_departures_percent,
      y = on_time_arrivals_percent,
      size = size_column,
      color = airline,
      text = paste(
        "Airline:", airline,
        "<br>On-Time Departures (%):", on_time_departures_percent,
        "<br>On-Time Arrivals (%):", on_time_arrivals_percent,
        "<br>Bubble Size (Weather Factor - ", input$weather_factor, "): ", size_column
      )
    )) +
      geom_point(alpha = 0.7) +
      labs(
        title = paste("Bubble Chart for", input$airline, "at", input$port),
        x = "On-Time Departures (%)",
        y = "On-Time Arrivals (%)",
        size = input$weather_factor
      ) +
      theme_minimal(base_size = 14)
    
    ggplotly(bubble_plot, tooltip = "text")
  })
  
  output$performancePlot <- renderPlotly({
    filtered <- filtered_data()
    
    if (nrow(filtered) == 0) {
      return(NULL)
    }
    
    performance_data <- data.frame(
      Category = c("On-Time Arrivals Rate", "On-Time Departures Rate", "Cancellations Rate", "Departures Delayed Rate", "Arrivals Delayed Rate"),
      Value = c(
        ifelse(is.na(mean(filtered$on_time_arrivals_percent, na.rm = TRUE)), 0, mean(filtered$on_time_arrivals_percent, na.rm = TRUE)),
        ifelse(is.na(mean(filtered$on_time_departures_percent, na.rm = TRUE)), 0, mean(filtered$on_time_departures_percent, na.rm = TRUE)),
        ifelse(is.na(mean(filtered$cancellations_percent, na.rm = TRUE)), 0, mean(filtered$cancellations_percent, na.rm = TRUE)),
        ifelse(is.na(sum(filtered$departures_delayed/filtered$sectors_scheduled, na.rm = TRUE)), 0, sum(filtered$departures_delayed/filtered$sectors_scheduled, na.rm = TRUE)),
        ifelse(is.na(sum(filtered$arrivals_delayed/filtered$sectors_scheduled, na.rm = TRUE)), 0, sum(filtered$arrivals_delayed/filtered$sectors_scheduled, na.rm = TRUE))
      )
    )
    
    p <- ggplot(performance_data, aes(x = Category, y = Value, fill = Category)) +
      geom_bar(stat = "identity") +
      geom_text(aes(label = round(Value, 1)), vjust = -1.5, color = "black", size = 4) +
      labs(
        title = paste("Performance Metrics for", input$airline, "at", input$port),
        x = "Performance Metric",
        y = "Value (%)"
      ) +
      theme_minimal(base_size = 14) +
      theme(
        plot.title = element_text(size = 18, face = "bold", color = "navy"),
        axis.text.x = element_text(angle = 45, hjust = 1, face = "italic"),
        axis.title.x = element_text(size = 16, face = "bold"),
        axis.title.y = element_text(size = 16, face = "bold"),
        plot.margin = margin(t = 50, r = 20, b = 40, l = 20),
        legend.position = "top"
      ) +
      scale_fill_viridis_d()
    
    ggplotly(p)
  })
  # create nodes 
  nodes_df <- data.frame(
    name = unique(c(bitre_filtered$arriving_port, bitre_filtered$departing_port))
  )
  
  # create links 
  links_df <- bitre_filtered %>%
    filter(!is.na(arriving_port) & !is.na(departing_port)) %>%
    group_by(departing_port, arriving_port) %>%
    summarise(value = n()) %>%
    ungroup() %>%
    mutate(
      source = match(departing_port, nodes_df$name) - 1,
      target = match(arriving_port, nodes_df$name) - 1
    ) %>%
    select(source, target, value)
  
  output$sankeyPlot <- renderSankeyNetwork({
    if (nrow(links_df) == 0 | nrow(nodes_df) == 0) {
      showNotification("No data available to display the Sankey diagram.", type = "error")
      return(NULL)
    }
    
    sankeyNetwork(
      Links = links_df, 
      Nodes = nodes_df,
      Source = "source", 
      Target = "target", 
      Value = "value",
      NodeID = "name", 
      units = "Flights",
      fontSize = 12, 
      nodeWidth = 30
    )
  })
  
  
  output$mapPlot <- renderPlotly({
    if (nrow(final_merged) == 0) {
      return(NULL)
    }
    
    fig <- plot_ly(
      final_merged, 
      lat = ~latitude, 
      lon = ~longitude, 
      type = 'scattermapbox', 
      mode = 'markers',
      marker = list(size = 10),
      text = ~paste("Airport:", airport_name, "<br>Average Temperature (°C):", average_annual_temperature_c, 
                    "<br>Airline:", airline, "<br>On-Time Arrivals (%):", on_time_arrivals_percent,
                    "<br>On-Time Departures (%):", on_time_departures_percent, "<br>Cancellations (%):", cancellations_percent)
    )
    
    fig <- fig %>% layout(
      mapbox = list(
        style = "open-street-map",
        zoom = 3,
        center = list(lat = -25.2744, lon = 133.7751) # Centered on Australia
      ),
      title = "Interactive Airport Weather and Flight Information Map"
    )
    
    fig
  })
}

shinyApp(ui = ui, server = server)